iT邦幫忙

2023 iThome 鐵人賽

DAY 16
0
自我挑戰組

Kotlin & Flutter App 開發比較思考日誌系列 第 16

[鐵人賽 Day 16] Kotlin & Flutter 元件比較(三) - 佈局相關常用元件實例

  • 分享至 

  • xImage
  •  

範圍

討論以下三種排列方式的布局元件:
1.垂直排列
2.橫向排列
3.網格排列

目的

比較類似功能的布局元件,於 Kotlin & Flutter 間的實作差異

1.垂直排列


實際執行結果

Kotlin Flutter
https://ithelp.ithome.com.tw/upload/images/20230920/20162686wc0AIlNC0O.jpg https://ithelp.ithome.com.tw/upload/images/20230920/201626866EMkIz3hj4.jpg

Kotlin

使用LinearLayout orientation="vertical"

調整兩個 layout 檔案

  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    tools:context=".MainActivity">
    
    <TextView
        android:id="@+id/TitleTV"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:text = "LinearLayout (vertical) "
        android:textSize = "20dp"
        android:textColor= "@color/black"
        />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintTop_toBottomOf="@id/TitleTV"
        >

        <include layout="@layout/card_item" />
        <include layout="@layout/card_item" />
        <include layout="@layout/card_item" />
    </LinearLayout>


</androidx.constraintlayout.widget.ConstraintLayout>
  • card_item.xml : 此 layout 會沿用下列其他範例
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cardView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:cardBackgroundColor="@color/white"
    app:cardCornerRadius="10dp"
    app:contentPadding="5dp"
    android:layout_margin="10dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/textView"
            android:text="My book"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:textColor= "@color/black"
            android:textSize="20dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="120dp"
            android:layout_height="80dp"
            android:paddingLeft="5dp"
            android:paddingRight="5dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/textView"
            app:srcCompat="@drawable/image_book" />

        <ImageView
            android:id="@+id/imageViewButton"
            android:layout_width="45dp"
            android:layout_height="45dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/imageView"
            app:srcCompat="@drawable/star"
            />
    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.cardview.widget.CardView>

Flutter

使用 Column 元件

修改 main.dart

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<StatefulWidget> {

  ///是否點擊收藏按鈕
  bool bIsCollectBook = false;

  @override
  Widget build(BuildContext context) {
    ///由上到下垂直排列元件 list
    List<Widget> _widgetList = [
      _wTitle("My book"),
      _wBookImage("asset/image_book.jpg"),
      _wButtonForCollectBook(bIsCollect: bIsCollectBook)
    ];

    ///圓角框元件內容
    Widget _wCardContent = Container(
        padding: EdgeInsets.symmetric(vertical: 5,horizontal: 3),
        constraints: BoxConstraints(maxWidth: 120),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisSize: MainAxisSize.min,
          children: _widgetList,
        ));

    return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: Scaffold(
            appBar: _wAppBar(),
            body: Container(
              alignment: Alignment.center,
              color: Colors.lightGreen.withOpacity(0.5),
              child: Column(children: [
                _wCardItem(_wCardContent),_wCardItem(_wCardContent),_wCardItem(_wCardContent)]),
            )));
  }

  PreferredSizeWidget _wAppBar(){
    return AppBar(
      backgroundColor: Colors.white,
      centerTitle: true,
      title: Text("Column",style: TextStyle(color:Colors.black,fontSize: 30,letterSpacing: 2,decoration: TextDecoration.underline),),
      elevation: 0,
    );
  }

  Widget _wCardItem(Widget wItem){
    return Card(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
      child: wItem,
    );
  }
  
  ///標題元件
  Widget _wTitle(String sTitle){
    return Text(
      "$sTitle",
      style: TextStyle(fontSize: 18,fontWeight: FontWeight.w500),
      textAlign: TextAlign.start,
    );
  }

  ///書籍圖片元件
  Widget _wBookImage(String sImagePath){
    return Container(
      alignment: Alignment.center,
      padding: EdgeInsets.symmetric(horizontal: 10,vertical: 10),
      child: Image.asset(
        "$sImagePath",
        height: 120,
        width: 80,
      ),
    );
  }

  ///收藏書籍按鈕元件
  Widget _wButtonForCollectBook({bool bIsCollect = false}){

    String sImagePath = bIsCollect ? "asset/star_select.png":"asset/star_unselect.png";

    return Container(
      alignment: Alignment.center,
      child: InkWell(
          onTap: () {
            ///點擊收藏書籍按鈕, 改變按鈕狀態 flag , 進而改變按鈕顏色
            setState(() {
              bIsCollectBook = !bIsCollectBook;
            });
          },
          child: Image.asset(
            "$sImagePath",
            height: 45,
            width: 45,
          )),
    );
  }


}

2.橫向排列


實際執行結果

Kotlin Flutter
https://ithelp.ithome.com.tw/upload/images/20230920/20162686BB0QnDEJIG.jpg https://ithelp.ithome.com.tw/upload/images/20230920/20162686rDhdHHivuW.jpg

Kotlin

使用 LinearLayout orientation="horizontal"

修改一個layout檔案

  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    tools:context=".MainActivity">
    
    <TextView
        android:id="@+id/TitleTV"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:text = "LinearLayout (horizontal) "
        android:textSize = "20dp"
        android:textColor= "@color/black"
        />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintTop_toBottomOf="@id/TitleTV"
        >

        <include layout="@layout/card_item" />
        <include layout="@layout/card_item" />
        <include layout="@layout/card_item" />
    </LinearLayout>


</androidx.constraintlayout.widget.ConstraintLayout>

Flutter

使用 Row 元件

修改 main.dart

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<StatefulWidget> {

  ///是否點擊收藏按鈕
  bool bIsCollectBook = false;

  @override
  Widget build(BuildContext context) {
    ///由上到下垂直排列元件 list
    List<Widget> _widgetList = [
      _wTitle("My book"),
      _wBookImage("asset/image_book.jpg"),
      _wButtonForCollectBook(bIsCollect: bIsCollectBook)
    ];

    ///圓角框元件內容
    Widget _wCardContent = Container(
        padding: EdgeInsets.symmetric(vertical: 5,horizontal: 3),
        constraints: BoxConstraints(maxWidth: 120),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisSize: MainAxisSize.min,
          children: _widgetList,
        ));

    return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: Scaffold(
            appBar: _wAppBar(),
            body: Container(
              alignment: Alignment.center,
              color: Colors.lightGreen.withOpacity(0.5),
              child: Row(children: [
                _wCardItem(_wCardContent),_wCardItem(_wCardContent),_wCardItem(_wCardContent)]),
            )));
  }

  PreferredSizeWidget _wAppBar(){
    return AppBar(
      backgroundColor: Colors.white,
      centerTitle: true,
      title: Text("Row",style: TextStyle(color:Colors.black,fontSize: 30,letterSpacing: 2,decoration: TextDecoration.underline),),
      elevation: 0,
    );
  }

  Widget _wCardItem(Widget wItem){
    return Card(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
      child: wItem,
    );
  }
  
  ///標題元件
  Widget _wTitle(String sTitle){
    return Text(
      "$sTitle",
      style: TextStyle(fontSize: 18,fontWeight: FontWeight.w500),
      textAlign: TextAlign.start,
    );
  }

  ///書籍圖片元件
  Widget _wBookImage(String sImagePath){
    return Container(
      alignment: Alignment.center,
      padding: EdgeInsets.symmetric(horizontal: 10,vertical: 10),
      child: Image.asset(
        "$sImagePath",
        height: 120,
        width: 80,
      ),
    );
  }

  ///收藏書籍按鈕元件
  Widget _wButtonForCollectBook({bool bIsCollect = false}){

    String sImagePath = bIsCollect ? "asset/star_select.png":"asset/star_unselect.png";

    return Container(
      alignment: Alignment.center,
      child: InkWell(
          onTap: () {
            ///點擊收藏書籍按鈕, 改變按鈕狀態 flag , 進而改變按鈕顏色
            setState(() {
              bIsCollectBook = !bIsCollectBook;
            });
          },
          child: Image.asset(
            "$sImagePath",
            height: 45,
            width: 45,
          )),
    );
  }


}

3.網格排列


實際執行結果

Kotlin Flutter
https://ithelp.ithome.com.tw/upload/images/20230920/20162686yyi5L4GSZd.jpg https://ithelp.ithome.com.tw/upload/images/20230920/2016268638KWAqfrgk.jpg

Kotlin

使用 GridLayout

修改一個layout檔案

  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    tools:context=".MainActivity">

    <TextView
        android:id="@+id/TitleTV"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="GridView"
        android:textColor="@color/black"
        android:textSize="20dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <GridLayout
        android:id="@+id/GridLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/TitleTV"
        >

        <include layout="@layout/card_item" />

        <include layout="@layout/card_item" />

        <include layout="@layout/card_item" />
    </GridLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Flutter

使用 GridView 元件

修改 main.dart

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<StatefulWidget> {

  ///是否點擊收藏按鈕
  bool bIsCollectBook = false;

  @override
  Widget build(BuildContext context) {
    ///由上到下垂直排列元件 list
    List<Widget> _widgetList = [
      _wTitle("My book"),
      _wBookImage("asset/image_book.jpg"),
      _wButtonForCollectBook(bIsCollect: bIsCollectBook)
    ];

    ///圓角框元件內容
    Widget _wCardContent = Container(
        padding: EdgeInsets.symmetric(vertical: 5,horizontal: 3),
        constraints: BoxConstraints(maxWidth: 120),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisSize: MainAxisSize.min,
          children: _widgetList,
        ));

    return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: Scaffold(
            appBar: _wAppBar(),
            body: Container(
              alignment: Alignment.center,
              color: Colors.lightGreen.withOpacity(0.5),
              child: GridView.count(
              crossAxisCount: 2,
              childAspectRatio: 0.9,
              children: [
                _wCardItem(_wCardContent),_wCardItem(_wCardContent),_wCardItem(_wCardContent)]),
            )));
  }

  PreferredSizeWidget _wAppBar(){
    return AppBar(
      backgroundColor: Colors.white,
      centerTitle: true,
      title: Text("GridVeiw",style: TextStyle(color:Colors.black,fontSize: 30,letterSpacing: 2,decoration: TextDecoration.underline),),
      elevation: 0,
    );
  }

  Widget _wCardItem(Widget wItem){
    return Card(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
      child: wItem,
    );
  }
  
  ///標題元件
  Widget _wTitle(String sTitle){
    return Text(
      "$sTitle",
      style: TextStyle(fontSize: 18,fontWeight: FontWeight.w500),
      textAlign: TextAlign.start,
    );
  }

  ///書籍圖片元件
  Widget _wBookImage(String sImagePath){
    return Container(
      alignment: Alignment.center,
      padding: EdgeInsets.symmetric(horizontal: 10,vertical: 10),
      child: Image.asset(
        "$sImagePath",
        height: 120,
        width: 80,
      ),
    );
  }

  ///收藏書籍按鈕元件
  Widget _wButtonForCollectBook({bool bIsCollect = false}){

    String sImagePath = bIsCollect ? "asset/star_select.png":"asset/star_unselect.png";

    return Container(
      alignment: Alignment.center,
      child: InkWell(
          onTap: () {
            ///點擊收藏書籍按鈕, 改變按鈕狀態 flag , 進而改變按鈕顏色
            setState(() {
              bIsCollectBook = !bIsCollectBook;
            });
          },
          child: Image.asset(
            "$sImagePath",
            height: 45,
            width: 45,
          )),
    );
  }


}


上一篇
[鐵人賽 Day 15] Kotlin & Flutter 元件比較(三) - 佈局相關常用元件名稱
下一篇
[鐵人賽 Day 17] Kotlin & Flutter 元件比較(三) - 佈局相關常用元件實例補充
系列文
Kotlin & Flutter App 開發比較思考日誌30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言